package codename.carrot.bomberman;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.ini4j.Ini;
import org.ini4j.Profile.Section;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.Log;
import codename.carrot.bomberman.game.networkobjects.DroidPoint;
import codename.carrot.bomberman.game.networkobjects.ItemContainer;
import codename.carrot.bomberman.models.CollectableItem;

/**
 *  A class that reads level-files from assets/levels
 * @author Group A4
 *
 */
public class LevelReader {
    /**
     * The sub-directory in assets which contains the levels
     */
    private final String LEVEL_DIR = "levels";
    /**
     * The calling context.
     */
    private Context context;
    /**
     * The parsed level after readLevel(String name) has been called
     */
    private LevelDefinition levelDefinition;
    
    /**
     * Constructs a level reader with the calling context.
     * @param context The calling context (activity).
     */
    public LevelReader(Context context) {
        this.context = context;        
    }          
    
    /**
     * Returns the parsed level in form of a {@link LevelDefinition}
     * @return The level definition
     */
    public LevelDefinition getLevelDefinition() {
        return levelDefinition;
    }
    
    /**
     *  Reads (and parses) a level-file, and creates the {@link LevelReader#levelDefinition}
     * @param levelName the name of the level
     * @throws Exception if the level dose not exist or if the level could not be read.
     */
    public void readLevel(String levelName) throws Exception {
        levelDefinition = new LevelDefinition();
        levelDefinition.setName(levelName);
        levelDefinition.setBoulders(new ArrayList<DroidPoint>());        
        levelDefinition.setItemContainers(new ArrayList<ItemContainer>());
        String[] levels = getLevelNames();        
        boolean levelExists = false;
        for(String s : levels) {
            Log.d("LEVEL", "Level: " + s);
            if(s.equals(levelName)) {
                levelExists = true;
                break;
            }
        }
        if(!levelExists) throw new Exception("Level does not exists");
        
        
        InputStream in = getLevelFile(levelName);
        if(in == null) throw new Exception("Could not read file!");
        
        Ini ini = new Ini(in);                
        Section generalSection = ini.get("General");
        levelDefinition.setHeight(Integer.parseInt(generalSection.get("height")));
        levelDefinition.setWidth(Integer.parseInt(generalSection.get("width")));
        
        String startPosBlue = generalSection.get("startPosBlue");
        String startPosYellow = generalSection.get("startPosYellow");
        String startPosOrange = generalSection.get("startPosOrange");
        String startPosPurple = generalSection.get("startPosPurple");
        
        setStartPos(LevelDefinition.COLOR_BLUE, startPosBlue);
        setStartPos(LevelDefinition.COLOR_YELLOW, startPosYellow);
        setStartPos(LevelDefinition.COLOR_ORANGE, startPosOrange);
        setStartPos(LevelDefinition.COLOR_PURPLE, startPosPurple);
                       
        Section boulderSection = ini.get("Boulders");
        Section itemContainerSection = ini.get("ItemContainers");
        List<String> boulderList = boulderSection.getAll("row");
        List<String> itemContainers = itemContainerSection.getAll("row");
        int row = 0;
        for(String s : boulderList) {
            addBoulders(row, s);
            row++;
        }        
        // Resetting the row counter..
        row = 0;
        for(String s : itemContainers) {
            addItemContainers(row, s);
            row++;
        }          
    }
   
    /**
     * Set the start position of the players.
     * @param color The color of the player.
     * @param startpos The start position to set.
     */
    private void setStartPos(int color, String startpos) {
        if(startpos == null) return;
        startpos = startpos.trim();
        String[] coords = startpos.split(",");
        try {
            int x = Integer.parseInt(coords[0]);
            int y = Integer.parseInt(coords[1]);
            DroidPoint dp = new DroidPoint(x, y);
            levelDefinition.setStartPos(color, dp);
        }
        catch(Exception e) {            
        }
    }
    
    /**
     * Add boulders to the level.
     * @param row the Y-coordinate on the level to add boulders to
     * @param boulders a commaseparated string with integers, representing columns on row <code>row</code> where boulders should be placed
     */
    private void addBoulders(int row, String boulders) {
        try {
            boulders = boulders.trim();
            if(boulders.equals("NONE")) return;
            String[] columns = boulders.split(",");
            for(String column : columns) {
                int xCoor = Integer.parseInt(column);
                levelDefinition.getBoulders().add(new DroidPoint(xCoor, row));
            }
        }
        catch(Exception e) {            
        }
    }
    
    /**
     * Add a container to the level.
     * @param row the Y-coordinate on the level to add itemContainers to
     * @param itemContainers a commaseparated string with integers, representing columns on row <code>row</code> where a container should be
     */
    private void addItemContainers(int row, String itemContainers) {
        try {
            itemContainers = itemContainers.trim();
            String[] columns = itemContainers.split(",");
            for(String column : columns) {                            
                int xCoor = Integer.parseInt(column);
                ItemContainer container = new ItemContainer(new DroidPoint(xCoor, row));
                Random random = new Random();
                int randomInt = random.nextInt();
                if (randomInt <=50) {
                    // Get random collectable item
                    int type = CollectableItem.getRandomType();
                    boolean isPowerUp = CollectableItem.getRandomIsPowerup(type);                    
                    container.setType(type, isPowerUp);
                }
                levelDefinition.getItemContainers().add(container);
            }
        }
        catch(Exception e) {
            
        }
    }
    
    /**
     * Read level from file.
     * @param levelName The name of the file.
     * @return The input stream.
     */
    private InputStream getLevelFile(String levelName) {
        Resources res = context.getResources();
        AssetManager am = res.getAssets();
        try {
            InputStream in = am.open(LEVEL_DIR + File.separatorChar + levelName);            
            return in;
        }
        catch(Exception e) {
            return null;
        }
    }       
    
    /**
     *  Returns an array of available level names
     * @return levelnames The names of the available levels. Returns null if none are found
     */
    public String[] getLevelNames() {
        Resources res = context.getResources();
        AssetManager am = res.getAssets();
        try {
            return am.list(LEVEL_DIR);            
        }
        catch(Exception e) {
            return null;
        }
    }       
}
